package Study1;
/* 多态
* 1. 直接赋值
* 2. 方法传参
* 3. 方法返回值
* */

import com.sun.xml.internal.messaging.saaj.soap.SOAPVersionMismatchException;

import javax.swing.text.StyledEditorKit;
import java.text.Normalizer;
import java.util.Scanner;

class Animal {
    public String name = "Animal";

    public void eat() {
        System.out.println("Animal eat");
    }
}


class Cats extends Animal {

    public String catName = "Cats";
    @Override
    public void eat() {  // 重写 父类中的 eat 的方法
        System.out.println("Cat::eat");
    }
    public void fuc() {
        System.out.println("cats");
    }


}

/*重写:
* 在继承的前提下
* 方法名与父类中的方法相同
* 返回值与父类中的返回值相同
* 参数列表与父类的参数列表相同
* 重载：
* 在同一个类中前提
* 方法名相同
* 参数列表不同(参数的个数，类型)
* 返回值不作要求
* */

class Animal2 {
    private String name;

    public Animal2(String name) {
        this.name = name;
        System.out.println("Animal2(构造方法)");
    }

    protected void eat() {
        System.out.println("Animal2 :: eat()");
    }
}


class Cat2 extends Animal2 {
    public int count = 99;

    public Cat2(String name) {  // 实例化子类，调用子类的构造方法的同时是默认调用父类的构造方法的
        super(name);
        System.out.println("Cat2(构造方法)");
    }

    @Override  // 注解：人可以理解，编译器同样可以读懂的注释
    protected void eat() {  // 重写子类的权限符要 大于等于 父类的权限符
        System.out.println("Cat2 :: eat()");
    }
}

// 重写的注意事项

// 1. 需要重写的方法，不能是被 final 修饰的，被 final 修饰后不能被修改的，它是密封的方法，不可以修改
// 同样被 final 修饰的类是不可以被 继承的
class Animal3 {
    public void eat() {

    }

}

class Cat3 extends Animal3 {
    /*
    @Override
    public void eat() {
        //  被 final 修饰的方法不可重写，被 final 修饰的类，不可继承
    }

    @Override
    public final void eat() {
        // 被 final 修饰的方法不可重写，被 final 修饰的类，不可继承
    }
    */
}


// 2.被重写的方法，访问的修饰符不可以是私有的 private,
// 书写的时候编译器不会报错，但是，当实例化的时候，你是无法访问的

class Animal4 {
//    @Override
    private void eat() {

    }
}

class Cat4 extends Animal4 {
//    @Override // 注解：人和编译器都可以理解的注释
    private void eat() {

    }
}


// 3. 被重写方法中，子类当中的访问修饰符是一定要 >= 父类中的访问修饰符的(除了 private 不可以修饰重写的方法，不会报错但无法使用)
class Animal5 {
    void eat() {
        System.out.println("默认权限修饰符");
    }
}

class Cat5 extends Animal5 {
    /*@Override  // 注解：人和编译器都可以理解的注释
    void eat() {
        System.out.println("重写：子类无权限修饰符 == 父类无权限修饰符");
    }*/

/*    @Override
    protected void eat(String name) {  // 重写的方法的参数列表要一致性

    }*/

  /*  @Override
    protected void eat() {
        System.out.println("重写 子类的protected权限修饰符 > 父类的无权限修饰符");
    }*/

    @Override
    public void eat() {
        System.out.println("重写：子类的public 权限修饰符 > 父类的无权修饰符 ");
    }
}


class Animal6 {
    protected void eat() {
        System.out.println("Animal6 ::eat");
    }
}


class Cat6 extends Animal6 {
  /*  @Override // 注解：人和编译器都可以理解的注释
    protected void eat() {
        System.out.println("子类中的权限修饰符 protected == 父类中的权限修饰符 protected");
    }*/

    @Override
    public void eat() {
        System.out.println("子类中的修饰符 public >= 父类中的权限修饰符 protected ");
    }
}


// 4.被 static 修饰的方式是不可以被 重写的
class Animal7 {
    public static void eat() {

    }
}

class Cat7 extends Animal7 {
   /* @Override // 重写注解：有利于，错误提醒
    public static void eat() {
        // 被 static 修饰的方法，静态方法不可以被重写的
    }*/



}


/*向下转型
向下转型是不太安全的，
因为你需要判断该父类是否是 某个子类的引用对象 使用 A instanceof B 判断 A 是不是 B的一个实例
向上转型：是子类对象转成 父类对象
向下转型：就是父类对象转成(强转)子类的对象，相比于向上转型来说，向下转型不常见，但是也有一定的用途
* */
class Animal8 {
    protected void eat() {
        System.out.println("Animal8 ::eat");
    }
}

class Cat8 extends Animal8 {
    @Override  // 重写的注解：错误提示，注解：人和编译器都可以读懂
    protected void eat() {  // 重写 动态绑定
        System.out.println("Cat8 :: eat");
    }
}

class Bird extends Animal8 {
    @Override // 重写 注解：错误提示
    protected void eat() {  // 重写 动态绑定
        System.out.println("Bird :: eat");
    }

    public void flg() {
        System.out.println("飞翔");
    }
}





// 理解多态实例 —— 打印图形
class Shape {
    protected void draw() {

    }
}

class Circle extends Shape {
    @Override // 重写，注解:人和编译器都可以读懂
    protected void draw() {
        System.out.println("画一个 🔴");
    }
}

class React extends Shape {
    @Override
    protected void draw() {
        System.out.println("画一个 🔺");
    }
}


//构造方法中调用重写的方法 (一个天坑)

class B {
    public B() {
        func();  // 在构造方法中调用重写的方法，动态绑定
    }

    protected void func() {
        System.out.println("B.func");
    }

}

class A extends B {
    @Override
    protected void func() {
        System.out.println("A.func");
    }
}




public class Study19 {

    // 创建一个Shape的对象的数组
    public static void main(String[] args) {
        Shape[] shape = {new Circle(),new React()};
        Shape[] shapes = new Shape[]{new Circle(),new React()};

        for (Shape shape2: shape) {
            shape2.draw();
        }

        System.out.println("************************");

        for ( Shape shape3: shapes) {
            shape3.draw();
        }
    }


    public static void main13(String[] args) {
        A a = new A();
        a.func();
        /*结论：
        * 尽量不要在构造方法中调用被子类重写的方法，(如果这个方法被子类重写，就会触发
        * 动态绑定)但是此时子类对象还没有构造完成，可能出现一些隐藏的，
        * 但是又极难发现的问题。*/
    }


    public static void func(Shape shape) {
        shape.draw();
    }
    public static void main12(String[] args) {
        Shape shape1 = new Circle();
        Shape shape2 = new React();
        func(shape1);
        func(shape2);
    }


    public static void main11 (String[] args) {
        Shape shape1 = new Circle();  // 向上转型，父类引用了 子类的对象，但是该父类不可以访问子类中的方法和字段
        Shape shape2 = new React();
        shape1.draw();
        shape2.draw();
    }


    public static void main10 (String[] args) {
        Animal8 animal8 = new Cat8();
        Animal8 animal81 = new Bird();
        animal8.eat();
       /* Bird bird = (Bird)animal8; // 这里报错，向下转型 该父类的引用是 Cat8() 不是 Bird 报错的，
        bird.eat();                 // 强转后访问 Bird的方法是会出现较为隐秘的错误的，一时半会是不容易找到的，应该
        bird.flg();                // 加上判断 是否为该引用 instanceof 类的引用,
        */
        // 改良
        if( animal8 instanceof Bird) {  // A instanceof B 判断 A 是否是B的实例化，引用对象，是 返回 true,不是 返回 false
            Bird bird = (Bird)animal8;
            bird.flg();
            bird.eat();
        } else {
            System.out.println("NO");
        }


        if(animal81 instanceof Bird) {
            Bird bird = (Bird)animal81;
            bird.eat();
            bird.flg();
        } else {
            System.out.println("NO NO ");
        }


    }


    public static void main9 (String[] args) {
        Animal7 animal7 = new Cat7();
        animal7.eat();
    }

    public static void main8(String[] args) {
        Animal6 animal6 = new Cat6(); // 向上转型，父类引用子类的对象，但是该父类无法访问属于子类中的字段和方法
        animal6.eat();

    }


    public static void main7 (String[] args) {
        Animal5 animal5 = new Cat5();
        animal5.eat();
    }

    public static void main6(String[] args) {
        Animal4 animal4 = new Cat4();
//        animal4.eat();
    }





    public static void main5(String[] args) {
        Animal2 animal2 = new Cat2("咪咪"); // 向上桩型，父类虽然引用了子类的对象，但是父类是无法访问子类的字段和方法的
        animal2.eat();
//        System.out.println(animal2.count); //
    }




    // 多态 —— 方法返回值
    /*注意：
    * 动态绑定是基于 子类对父类的存在的方法的重写的前提下的
    * 字段是没有重写的，也就是没有动态绑定的，*/
    public static Animal func() {
        Animal animal = new Cats(); // 向上转型，父类对子类的引用对象，

        return animal;
    }

    public static void main4(String[] args) {
        func().eat();
        System.out.println(func().name);

    }



    // 多态 —— 方法传参
    public static void func(Animal animal) { // 向上转型 ，父类对引用子类的引用 但该父类无法访问 子类中字段，方法
        animal.eat();      // 重写 —— 动态绑定
        System.out.println(animal.name);
        // 静态方法可以相互访问，不可以直接访问非静态的方法，需要实例化

    }

    public static void main3(String[] args) {
        Animal animal = new Cats();  // 向上转型，父类虽然引用子类的引用，但无法访问子类中的字段和方法
        func(animal);
        System.out.println("******************");
        Cats cats = new Cats();
        func(cats);

    }


    // 多态 —— 直接赋值
    public static void main2(String[] args) {
        Animal animal = new Animal();
        System.out.println(animal.name);
        animal.eat();

        System.out.println("***************");

        Animal animal2 = new Cats();   // 向上转型，父类引用子类的对象，多态 直接赋值
        animal2.eat();                 // 重写 - 动态绑定
        System.out.println(animal2.name);
//        animal2.fuc(); // 向上转型 父类虽然引用子类的对象，但是 该父类类型是无法访问，子类中的字段和不是重写的方法的


    }

    public static void main1 (String[] args) {
        Animal animal = new Animal();
        System.out.println(animal.name);
        animal.eat();
        System.out.println("***************");

        Cats cats = new Cats();
        System.out.println(cats.name);
        cats.eat();
        cats.fuc();
    }

}
